/**
YASRACS game kliens fo osztaly.

YASTACS game
Copyright (C) 2012 Kalman Kiss, kiskami@freemail.hu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Contact author at: kiskami@freemail.hu or
Hungary, 8900 Zalaegerszeg
Kakukkfu u 4.
*/
module App;

import std.stdio;
import std.getopt;

import iCore;
import rendering.RenderSystem;
import input.SystemInput;
import gui.Gui;

import ogreint.Timer;
import ogreint.KeyCode;

import intro.Intro;
import startmenu.StartMenu;

import client.Client;

import Common;

immutable int DEF_GRAPHRESWIDTH = 640;
immutable int DEF_GRAPHRESHEIGHT = 480;
immutable bool DEF_FULLSCREENGRAPH = false;

/// A jatek fo osztalya.
final class App : iCore {

	/**
	1. parancssori parameterek ertelmezese, konfiguracio

	*/
	this(string[] argv) {
		writeln(name,", version ",ver,"\n",copyright,"\n",licence);

		exitMainLoop = false;
		haveWindowFocus = true;

		reqwidth = DEF_GRAPHRESWIDTH;
		reqheight = DEF_GRAPHRESHEIGHT;
		fullscreen = DEF_FULLSCREENGRAPH;
		bool help = false;

		getopt(argv,
			   "width|w",  &reqwidth, 
			   "height|h", &reqheight,
			   "fullscreen|f", &fullscreen,
			   "help|?|h", &help);

		if(help) {
			writeln("Command line parameters: \n"
			, "-width|w requested graphical resolution width\n"
			, "-height|h requested graphical resolution height\n"
			, "-fullscreen|f full screen mode");
			exitMainLoop = true;
		}
	}

	/**
	2. alrendszerek initje és main loop
	*/
	int run() {

		// korai kilépés kell? help cmd line param
		if(exitMainLoop) return 0;

		gamestate = eGameStates.STARTUP;

		// logger?

		initScripting();

		initRenderSystem();
		// input must be initialized AFTER render system, 
		// because it needs the renderwindow
		initInputSystem();
		// gui must be initialized AFTER render and input system, 
		// because it needs the renderwindow and input to function
		initGui();

		initAudioSystem();

		initPhysics();

		initNetwork();

		// start with intro
		toGameState(gamestate,eGameStates.INTRO);

		// *** main loop ********************************************
		double deltatime=0
//			, fps=0, minfps=1000000, maxfps=0
			,fpst=0;
		int framecount=0;
		Timer maintimer = new Timer;
//		maintimer.reset();
		do {
			input.captureInputDevices();

//			if(haveWindowFocus) {
				if(currstate !is null)
					currstate.update(deltatime);

				// ideiglenesen - egy vészkilépési lehetőség
				debug if(input.keyPressed(KeyCode.KC_ESCAPE))  {
					endMainLoop();
				}

				gui.Update(deltatime);
//			}

			rendersystem.Update();

			if(input.keyPressed(KeyCode.KC_F11)) {
				auto f = rendersystem.screenshotToFile();
				debug writeln("Wrote a screenshot into file ", f);
			}

			deltatime = maintimer.getMicroseconds()/1000000.0;
			maintimer.reset();

			// fps kalkuláció
			fpst += deltatime;
			++framecount;
			if(fpst>=2) {
//				fps = framecount / fpst;
//				if(fps<minfps) minfps = fps;
//				if(fps>maxfps) maxfps = fps;
				fpst = 0;
				framecount = 0;
				debug {
					auto rw = rendersystem.renderwindow;
					writeln("Fps: ",rw.getLastFPS()
							,", min: ",rw.getWorstFPS()
							," max:",rw.getBestFPS()
							,"; batches: ", rw.getBatchCount()
							, ", tris: ", rw.getTriangleCount());
				}
			}
		} while (!exitMainLoop);

		return 0;
	}

	~this() {

	}

	void endMainLoop() {
		exitMainLoop = true;
	}

	void windowFocusChange() {
		haveWindowFocus = !haveWindowFocus;
		if(!haveWindowFocus) {
			mouseshown = gui.showMouseCursor(true);
		} else {
			mouseshown = gui.showMouseCursor(mouseshown);
		}
	}

	void toGameState(eGameStates from, eGameStates to) {
		debug writeln ("Gamestate transition from ", from, " to ", to);
		switch(from) {
			default:
				throw new Exception("Unknown state transition");
			case eGameStates.STARTUP : 
				if(to != eGameStates.INTRO) 
					throw new Exception("Unknown state transition");

				intro = new Intro(this);
				intro.initialize();

				currstate = intro;
				gamestate = to;
				break;
			case eGameStates.INTRO :
				if(to != eGameStates.STARTMENU) 
					throw new Exception("Unknown state transition");

				intro.deinitialize();

				startmenu = new StartMenu(this);
				startmenu.initialize();

				currstate = startmenu;
				gamestate = to;
				break;
			case eGameStates.STARTMENU :
				if(to != eGameStates.INGAME
				   && to != eGameStates.EXIT
				   && to != eGameStates.ABOUT) 
					throw new Exception("Unknown state transition in App.toGameState!");

				startmenu.deinitialize();

				switch(to) {
				case eGameStates.INGAME:
					// A client itt már létrehozva kell legyen!
					client_.initialize();

					currstate = client_;
					gamestate = to;
					break;
				case eGameStates.EXIT:
					endMainLoop();
					currstate = null;
					gamestate = to;
					break;
				case eGameStates.ABOUT:
					break;
				default:
					throw new Exception("Unknown state transition");
//					break;
				}

				break;
			case eGameStates.INGAME:
				// a startmenu-be lehet visszalépni a játékból
				if(to != eGameStates.STARTMENU) 
					throw new Exception("Unknown state transition");

				client_.deinitialize();

				startmenu.initialize();

				currstate = startmenu;
				gamestate = to;
				break;
			case eGameStates.EXIT :
				endMainLoop();
				gamestate = to;
				break;
			case eGameStates.ABOUT :

				break;
		}
	}

	@property RenderSystem rendersystem() { return rendersystem_; }
	@property Gui gui() { return Gui.instance(); }
	@property SystemInput input() { return SystemInput.instance(); }
	@property void client(Client c) { client_ = c; }

private:		

	int reqwidth,
		reqheight;
	bool fullscreen;

	RenderSystem rendersystem_;

	bool exitMainLoop
		, haveWindowFocus
		, mouseshown;

	eGameStates gamestate;
	GameState currstate;

	Intro intro;
	StartMenu startmenu;

	Client client_;

	void initScripting() {
//		debug write("Initializing scripting...");

//		debug scope(exit) writeln("ok");
	}

	void initRenderSystem() {
		debug write("Initializing rendersystem...");
		rendersystem_ = new RenderSystem(name,reqwidth,reqheight,fullscreen);
		debug scope(exit) writeln("ok");
	}

	void initGui() {
		debug write("Initializing gui...");

		gui.setCore(this);
		gui.initialize();

		// előre betöltünk néhány fontos erőforrást
		gui.loadFont("BlueHighway-Bold-48.font");
		gui.loadFont("BlueHighway-Bold-8.font");
		gui.loadFont("BlueHighway-Bold-12.font");
		gui.loadFont("BlueHighway-Bold-10.font");
		gui.loadFont("BlueHighway-10.font");
		gui.loadFont("BlueHighway-12.font");
		gui.loadFont("BlueHighway-8.font");

		gui.loadImageSet("TitleImages.imageset");
		// -----------------------------------------

		input.sendInputToGui();

		rendersystem.Update();

		debug scope(exit) writeln("ok");
	}

	void initInputSystem() {
		debug write("Initializing input system...");
		// renderer have to be initialized and renderwindow created
		input.setCore(this);
		// AFTER setCore have to call this:
		input.initInputEvents();

		debug scope(exit) writeln("ok");
	}

	void initAudioSystem() {
//		debug write("Initializing audio system...");

//		debug scope(exit) writeln("ok");

	}

	void initPhysics() {
//		debug write("Initializing physics...");

//		debug scope(exit) writeln("ok");
	}

	void initNetwork() {
//		debug write("Initializing networking...");

//		debug scope(exit) writeln("ok");
	}

}


